﻿using System;
using System.Windows.Automation;
using System.Windows;
using System.Runtime.InteropServices;

namespace AutoWinUI
{
    public enum SearchScope { Children, Descendants };

    class AutoElement
    {
        const int SearchTimeInSecond = 30;

        private string _AutomationID = string.Empty;

        public string AutomationID
        {
            set { _AutomationID = value; }
        }

        private string _ClassName = string.Empty;

        public string ClassName
        {
            set { _ClassName = value; }
        }

        private string name = string.Empty;

        public string Name
        {
            set { name = value; }
        }

        AutomationElement _ParentElement = null;

        public AutomationElement ParentElement
        {
            set { _ParentElement = value; }
        }

        

        public AutomationElement FindFirst(SearchScope scope)
        {
            if ( _ParentElement==null)
                throw new ElementNotAvailableException("Parent element cannot be null!");

            AutomationElement target = null;

            PropertyCondition proAutomationID = new PropertyCondition(AutomationElement.AutomationIdProperty, _AutomationID);
            PropertyCondition proClassName = new PropertyCondition(AutomationElement.ClassNameProperty, _ClassName);
            //PropertyCondition proName = new PropertyCondition(AutomationElement.ClassNameProperty, name);
            AndCondition andCond = new AndCondition(proAutomationID, proClassName );

            DateTime time1 = DateTime.Now.AddSeconds(SearchTimeInSecond);

            CacheRequest cacheRequest = new CacheRequest();
            cacheRequest.TreeFilter = Automation.RawViewCondition;

            using (cacheRequest.Activate())
            {
                while (DateTime.Now < time1)
                {
                    if (scope == SearchScope.Children)
                    {
                        // target = _ParentElement.FindFirst(TreeScope.Children, andCond);
                        target = _ParentElement.FindFirst(TreeScope.Children, andCond);
                    }
                    else if (scope == SearchScope.Descendants)
                    {
                        // target = _ParentElement.FindFirst(TreeScope.Descendants, andCond);
                        target = _ParentElement.FindFirst(TreeScope.Descendants, andCond);
                    }
                        
                    if (target != null)
                        break;

                    System.Threading.Thread.Sleep(100);
                }
            }
           
            //if (target == null)
            //    throw new ElementNotAvailableException("No element is found!");

            return target;
        }

        public AutomationElement FindFirst2(SearchScope scope)
        {
            if (_ParentElement == null)
                throw new ElementNotAvailableException("Parent element cannot be null!");

            AutomationElement target = null;

            PropertyCondition proAutomationID = new PropertyCondition(AutomationElement.AutomationIdProperty, _AutomationID);
            PropertyCondition proClassName = new PropertyCondition(AutomationElement.ClassNameProperty, _ClassName);
            PropertyCondition proName = new PropertyCondition(AutomationElement.ClassNameProperty, name);
            AndCondition andCond = new AndCondition(proAutomationID, proClassName, proName);

            DateTime time1 = DateTime.Now.AddSeconds(SearchTimeInSecond);

            CacheRequest cacheRequest = new CacheRequest();
            cacheRequest.TreeFilter = Automation.RawViewCondition;

            using (cacheRequest.Activate())
            {
                while (DateTime.Now < time1)
                {
                    if (scope == SearchScope.Children)
                    {
                        // target = _ParentElement.FindFirst(TreeScope.Children, andCond);
                        target = _ParentElement.FindFirst(TreeScope.Children, andCond);
                    }
                    else if (scope == SearchScope.Descendants)
                    {
                        // target = _ParentElement.FindFirst(TreeScope.Descendants, andCond);
                        target = _ParentElement.FindFirst(TreeScope.Descendants, andCond);
                    }

                    if (target != null)
                        break;

                    System.Threading.Thread.Sleep(100);
                }
            }

            //if (target == null)
            //    throw new ElementNotAvailableException("No element is found!");

            return target;
        }

        public AutomationElementCollection FindAll(SearchScope scope)
        {
            if (_ParentElement == null)
                throw new ElementNotAvailableException("Parent element cannot be null!");

            AutomationElementCollection target = null;

            PropertyCondition proAutomationID = new PropertyCondition(AutomationElement.AutomationIdProperty, _AutomationID);
            PropertyCondition proClassName = new PropertyCondition(AutomationElement.ClassNameProperty, _ClassName);
            PropertyCondition proName = new PropertyCondition(AutomationElement.ClassNameProperty, name);
            AndCondition andCond = new AndCondition(proAutomationID, proClassName, proName);

            DateTime time1 = DateTime.Now.AddSeconds(SearchTimeInSecond);

            while (DateTime.Now < time1)
            {
                if (scope == SearchScope.Children)
                    target = _ParentElement.FindAll(TreeScope.Children, andCond);
                else if (scope == SearchScope.Descendants)
                    target = _ParentElement.FindAll(TreeScope.Descendants, andCond);

                if (target != null)
                    break;

                System.Threading.Thread.Sleep(100);
            }

            if (target == null)
                throw new ElementNotAvailableException("No element is found!");

            return target;
        }

        /// <summary>
        /// Tries to get clickable point of automation element
        /// </summary>
        /// <remarks>
        /// Algorithm:
        /// 1. get bounding rectangle of automation element
        /// 2. clickable point is located is the middle of bounding rectangle
        /// </remarks>
        /// <param name="clickablePoint">(out parameter) clickable point of Point type if try was successfull</param>
        /// <returns>true if clickable point was obtained successfully, false otherwise</returns>
        public bool TryGetClickablePoint(AutomationElement automationElement, out Point clickablePoint)
        {
            bool isClickablePointObtained = false;
            clickablePoint = new Point();

            if (automationElement != null)
            {
                try
                {
                    double clickableX = -1, clickableY = -1;
                    Rect boundingRectangle = automationElement.Current.BoundingRectangle;
                    clickableX = (boundingRectangle.Left + boundingRectangle.Right) / 2;
                    clickableY = (boundingRectangle.Top + boundingRectangle.Bottom) / 2;
                    clickablePoint = new Point(clickableX, clickableY);
                    isClickablePointObtained = true;
                }
                catch { }
            }

            return isClickablePointObtained;
        }

        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        private static extern void mouse_event(long dwFlags, long dx, long dy, long dwData, long dwExtraInfo);

        /// <summary>
        /// constant for mouse event "left button down"
        /// </summary>
        private const int MOUSEEVENTF_LEFTDOWN = 0x02;

        /// <summary>
        /// constant for mouse event "left button up"
        /// </summary>
        private const int MOUSEEVENTF_LEFTUP = 0x04;

        public void MouseLeftClick(int x, int y)
        {
            System.Drawing.Point current = System.Windows.Forms.Cursor.Position;
            System.Windows.Forms.Cursor.Position = new System.Drawing.Point(x, y);
            mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, x, y, 0, 0);
            System.Windows.Forms.Cursor.Position = current;
        }
    }


}
